package sudoku.support

import java.util.*


/**
 * https://zhuanlan.zhihu.com/p/208768786
 *
 * @author flutterdash@qq.com
 * @since 2021/10/1 12:46
 */

const val normalStyle = "\u001b[0m"
private var borderStyle = StyleTemplate(FontProp.bold, ColorPreset.deepGray, ColorPreset.lightGrey)
private var fixedStyle = StyleTemplate(FontProp.bold, ColorPreset.black, ColorPreset.lightGrey)
private var unsetStyle = StyleTemplate(FontProp.default, ColorPreset.gray, ColorPreset.lightGrey)
private var spaceStyle = StyleTemplate(FontProp.default, ColorPreset.lightGrey, ColorPreset.lightGrey)
private var spaceCell = CellDesc(" ", spaceStyle)

data class CellDesc(
    var text: String,
    var style: StyleTemplate
) {
    fun copy(): CellDesc = CellDesc(text, style.copy())

    override fun toString(): String {
        return "$style$text"
    }
}

data class StyleTemplate(
    var prop: Int,
    var prospect: Int,
    var background: Int
) {
    fun copy(): StyleTemplate {
        return StyleTemplate(prop, prospect, background)
    }

    fun onSearch() {
        if (background == ColorPreset.deepGray) return
        background = ColorPreset.gray
    }

    fun onHit() {
        prospect = ColorPreset.green
        background = ColorPreset.deepGray
    }

    override fun toString(): String {
        return "\u001B[38;5;${prospect}m\u001B[48;5;${background}m"
    }
}

object FontProp {
    const val default = 0
    const val bold = 1
    const val underline = 4
//    const val flashing = 5
//    const val reverse = 7
//    const val hide = 8
}

object ColorPreset {
    const val gray = 250
    const val lightGrey = 253
    const val deepGray = 243
    const val lightRed = 199
    const val green = 76
    const val red = 128
    const val error = 160
    const val black = 235
}

data class Cell(
    var styles: Stack<CellDesc> = Stack(),
    var prefix: CellDesc = spaceCell
) {
    inline fun pushStyle(init: CellDesc.() -> Unit) {
        val newStyle = styles.peek().copy()
        newStyle.init()
        styles.push(newStyle)
    }

    fun removeStyle() {
        styles.pop()
    }

    override fun toString(): String {
        return "$prefix${styles.peek()}"
    }
}

object VisualBoard {
    private val buff = StringBuilder()
    private val vBoard = Array(side) { Array(side) { Cell() } }
    private val realBoard = Array(side + 2) {
        Array(side + 2) { Cell() }
    }

    init {
        redirect()
    }

    fun init(input: Board) {
        fun getCellDescOf(num: Int): CellDesc {
            return when (num) {
                -1, -2, -3 -> CellDesc(switchPlaceholder(num), borderStyle)
                0 -> CellDesc(switchPlaceholder(num), unsetStyle)
                else -> CellDesc(switchPlaceholder(num), fixedStyle)
            }
        }

        val placeholderBoard = buildPlaceholderBoard(input)
        for (r in placeholderBoard.indices) {
            for (c in placeholderBoard[r].indices) {
                val cell = realBoard[r][c].styles
                cell.clear()
                cell.push(getCellDescOf(placeholderBoard[r][c]))
            }
        }
        redirect()
    }

    operator fun get(row: Int, col: Int): Cell {
        return vBoard[row][col]
    }

    fun showCurrentBoard(): String {
        buff.clear()
        for (r in realBoard.indices) {
            for (c in realBoard[r].indices) {
                buff.append(realBoard[r][c])
            }
            buff.append("$normalStyle\n")
        }
        return buff.toString()
    }

    private fun redirect() {
        fun map(num: Int): Int {
            if (num < 3) return num
            if (num > 5) return num + 2
            return num + 1
        }
        for (r in vBoard.indices) {
            for (c in vBoard[r].indices) {
                vBoard[r][c] = realBoard[map(r)][map(c)]
            }
        }
    }
}

fun main() {
    VisualBoard.init(defaultTestBoard())
    println(VisualBoard.showCurrentBoard())
}
